package com.ht.demo.service.system;

import com.ht.demo.common.appexception.AppErrorCodeEnum;
import com.ht.demo.common.appexception.AppException;
import com.ht.demo.common.constans.SystemConstant;
import com.ht.demo.common.enums.LoginStatusEnum;
import com.ht.demo.common.pojo.bo.system.SysRoleBO;
import com.ht.demo.common.pojo.bo.system.SysUserBO;
import com.ht.demo.common.pojo.vo.LoginBodyVO;
import com.ht.demo.common.util.DateUtil;
import com.ht.demo.common.util.MD5Util;
import com.ht.demo.common.util.enums.DateStyleEnum;
import com.ht.demo.dao.dao.system.SysUserRoleDao;
import com.ht.demo.dao.domain.system.SysRoleDO;
import com.ht.demo.dao.domain.system.SysUserRoleDO;
import com.ht.demo.service.constans.SystemConfig;
import com.ht.demo.service.convert.system.SysRoleBeanUtil;
import com.ht.demo.service.convert.system.SysUserBeanUtil;
import com.ht.demo.service.manager.async.AsyncManager;
import com.ht.demo.service.manager.async.factory.AsyncFactory;
import com.ht.demo.service.mapper.system.SysRoleMapper;
import com.ht.demo.service.mapper.system.SysUserMapper;
import com.ht.demo.service.redis.RedisKeyUtil;
import com.ht.demo.service.redis.RedisUtil;
import com.ht.demo.service.redis.TimeUtil;
import com.ht.demo.service.util.ServletUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Locale;

/**
 * @Author hdengxian
 * @Date 2023/3/27 17:18
 */
@Component
public class UserTokenManager {
    private Logger logger = LoggerFactory.getLogger(UserTokenManager.class);

    @Resource
    private SysRoleMapper sysRoleMapper;
    @Resource
    private SysUserRoleDao sysUserRoleDao;
    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private RedisUtil redisUtil;

    private static class CreateUserTokenManager{
        private static final UserTokenManager INSTANCE = AppContext.getBean(UserTokenManager.class);
    }

    public static UserTokenManager getInstance() {
        return CreateUserTokenManager.INSTANCE;
    }

    public SysUserBO userLogin(LoginBodyVO body) throws AppException {
        if (body == null) {
            throw new AppException(AppErrorCodeEnum.ACCOUNT_RO_PASS_WD_ERROR);
        }
        if (StringUtils.isEmpty(body.getUsername()) || StringUtils.isEmpty(body.getPassword())) {
            throw new AppException(AppErrorCodeEnum.ACCOUNT_RO_PASS_WD_ERROR);
        }
        //校验验证码
        validateCaptcha(body.getCode(), body.getUuid());

        SysUserBO userBO = SysUserBeanUtil.sysUserDOToBO(sysUserMapper.findUserByLoginNo(body.getUsername()));
        if (userBO == null) {
            throw new AppException(AppErrorCodeEnum.LOGIN_NO_NOT_EXIST);
        }
        if (!MD5Util.passwordMD5(body.getPassword(), SystemConfig.passwordMd5Key).equals(userBO.getPassWdMd())) {
            throw new AppException(AppErrorCodeEnum.ACCOUNT_RO_PASS_WD_ERROR);
        }
        StringBuffer buffer = new StringBuffer(DateUtil.DateToString(new Date(), DateStyleEnum.YYYY_MM_DD_HH_MM_SS_STR));
        buffer.append(userBO.getLoginNo());
        String token = MD5Util.encryptionToMD5(buffer.toString()).toUpperCase(Locale.ROOT);
        userBO.setToken(token);

        //查询用户角色
        SysUserRoleDO userRole = sysUserRoleDao.findByUserId(userBO.getSysUserId());
        userBO.setSysRoleId(userRole.getSysRoleId());
        SysRoleBO role = SysRoleBeanUtil.sysRoleDOToBO(sysRoleMapper.findById(userRole.getSysRoleId()));
        userBO.setRole(role);

        //存入redis30分钟后过期
        redisUtil.setex(RedisKeyUtil.LOGIN_TOKEN_KEY + token, userBO, TimeUtil.MINUTES_30);
        logger.info("用户：【{}】 登录系统成功，账号：{}, token：{}", userBO.getNickName(), userBO.getLoginNo(), token);

        //异步执行，存储登录记录
        AsyncManager.getInstance().execute(AsyncFactory.recordLoginInfo(userBO, LoginStatusEnum.LOGIN_SUCCESS.getCode()
                , LoginStatusEnum.LOGIN_SUCCESS.getDesc(), token));
        return userBO;
    }

    /**
     * 验证验证码
     * @param code
     * @param uuid
     */
    public void validateCaptcha(String code, String uuid) {
        if (!SystemConfig.getCaptchaEnabled()) {
            return;
        }
        if (StringUtils.isBlank(code)) {
            throw new AppException(AppErrorCodeEnum.CAPTCHA_CODE_ERROR);
        }
        String redisKey = RedisKeyUtil.CAPTCHA_CODE_KEY + uuid;
        String value = redisUtil.get(redisKey);
        if (StringUtils.isBlank(value)) {
            throw new AppException(AppErrorCodeEnum.CAPTCHA_CODE_TIMEOUT);
        }
        if (!code.toLowerCase(Locale.ROOT).equals(value.toLowerCase(Locale.ROOT))) {
            redisUtil.delete(redisKey);
            throw new AppException(AppErrorCodeEnum.CAPTCHA_CODE_ERROR);
        }
        redisUtil.delete(redisKey);
    }


    public Boolean userExitLogin(String token) throws AppException {
        try {
            SysUserBO loginUser = getLoginUser(token);
            if (loginUser != null) {
                logger.info("用户【{}】主动退出登录，token【{}】", loginUser.getNickName(), token);
            } else {
                logger.info("token【{}】退出登录，未找到登录信息", token);
            }

            //异步执行，存储登录记录
            AsyncManager.getInstance().execute(AsyncFactory.recordLoginInfo(loginUser, LoginStatusEnum.LOGOUT.getCode()
                    , LoginStatusEnum.LOGOUT.getDesc(), token));

        } catch (Exception e) {
            logger.info("token【{}】被动退出登录，未找到登录信息", token);
        }
        redisUtil.delete(RedisKeyUtil.LOGIN_TOKEN_KEY + token);


        return Boolean.TRUE;
    }


    public SysUserBO getLoginUser(String token, HttpServletRequest request) throws AppException {
        SysUserBO userBO = redisUtil.get(RedisKeyUtil.LOGIN_TOKEN_KEY + token, SysUserBO.class);
        if (userBO == null) {
            logger.error("UserTokenManager.getLoginUser - 需要登录 - url={}", request.getRequestURI());
            throw new AppException(AppErrorCodeEnum.UN_LOGIN);
        }
        //重置redis30分钟后过期
        redisUtil.setex(RedisKeyUtil.LOGIN_TOKEN_KEY + token, userBO, TimeUtil.MINUTES_30);
        return userBO;
    }

    public SysUserBO getLoginUser(String token) throws AppException {
        SysUserBO userBO = redisUtil.get(RedisKeyUtil.LOGIN_TOKEN_KEY + token, SysUserBO.class);
        if (userBO == null) {
            throw new AppException(AppErrorCodeEnum.UN_LOGIN);
        }
        //重置redis30分钟后过期
        redisUtil.setex(RedisKeyUtil.LOGIN_TOKEN_KEY + token, userBO, TimeUtil.MINUTES_30);
        return userBO;
    }

    public SysUserBO getLoginUser() throws AppException {
        String token = ServletUtils.getRequest().getHeader(SystemConstant.TOKEN_NAME);
        SysUserBO userBO = redisUtil.get(RedisKeyUtil.LOGIN_TOKEN_KEY + token, SysUserBO.class);
        if (userBO == null) {
            throw new AppException(AppErrorCodeEnum.UN_LOGIN);
        }
        //重置redis30分钟后过期
        redisUtil.setex(RedisKeyUtil.LOGIN_TOKEN_KEY + token, userBO, TimeUtil.MINUTES_30);
        return userBO;
    }

}
